﻿// -----------------------------------------------------------------------
// <copyright file="FileInfo.cs" company="Company">
// Company 2011
// </copyright>
// -----------------------------------------------------------------------

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Security.AccessControl;
using Company.IO.Interfaces;
using FileAccess = Company.IO.Interfaces.FileAccess;
using FileAttributes = Company.IO.Interfaces.FileAttributes;
using FileMode = Company.IO.Interfaces.FileMode;
using FileShare = Company.IO.Interfaces.FileShare;

namespace Company.System.IO
{
    /// <summary>
    /// Provides properties and instance methods for the creation, copying, deletion, moving, and opening of files, and aids in the creation of FileStream objects. This class cannot be inherited.
    /// </summary>
    [Guid("0671e0ea-1467-4246-814c-4e68e080e9fe")]
    [Serializable]
    public sealed class FileInfo : IFileInfo
    {
        /// <summary>
        /// Initializes a new instance of the FileInfo class
        /// </summary>
        /// <param name="systemIoConverter">The converter to use</param>
        /// <param name="fileInfo">The FileInfo object</param>
        public FileInfo(ISystemIoConverter systemIoConverter, global::System.IO.FileInfo fileInfo)
        {
            IoConverter = systemIoConverter;
            InternalFileInfo = fileInfo;
        }

        #region IFileInfo Properties

        /// <summary>
        /// For files, gets the name of the file.
        /// For directories, gets the name of the last directory in the hierarchy if a hierarchy exists.
        /// Otherwise, the Name property gets the name of the directory.
        /// </summary>
        /// <value>
        /// A string that is the name of the parent directory,
        /// the name of the last directory in the hierarchy,
        /// or the name of a file, including the file name extension.
        /// </value>
        string IFileSystemInfo.Name
        {
            get { return InternalFileInfo.Name; }
        }

        /// <summary>
        /// Gets the size, in bytes, of the current file.
        /// </summary>
        long IFileInfo.Length
        {
            get { return InternalFileInfo.Length; }
        }

        /// <summary>
        /// Gets a string representing the directory's full path.
        /// </summary>
        string IFileInfo.DirectoryName
        {
            get { return InternalFileInfo.DirectoryName; }
        }

        /// <summary>
        /// Gets an instance of the parent directory.
        /// </summary>
        IDirectoryInfo IFileInfo.Directory
        {
            get { return Convert(InternalFileInfo.Directory); }
        }

        /// <summary>
        /// Gets or sets a value that determines if the current file is read only.
        /// </summary>
        bool IFileInfo.IsReadOnly
        {
            get { return InternalFileInfo.IsReadOnly; }

            set { InternalFileInfo.IsReadOnly = value; }
        }

        /// <summary>
        /// Gets a value indicating whether the file or directory exists.
        /// </summary>
        /// <value>
        /// true if the file or directory exists; otherwise, false.
        /// </value>
        bool IFileSystemInfo.Exists
        {
            get { return InternalFileInfo.Exists; }
        }

        #endregion

        #region IFileSystemInfo Properties

        /// <summary>
        /// Get or set the internal FileInfo object
        /// </summary>
        private global::System.IO.FileInfo InternalFileInfo { get; set; }

        /// <summary>
        /// Get or set the converter
        /// </summary>
        private ISystemIoConverter IoConverter { get; set; }

        /// <summary>
        /// Gets the full path of the directory or file.
        /// </summary>
        /// <value>
        /// A string containing the full path.
        /// </value>
        /// <exception cref="IPathTooLongException">
        /// The fully qualified path and file name is 260 or more characters.
        /// </exception>
        /// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission.</exception>
        string IFileSystemInfo.FullName
        {
            get { return InternalFileInfo.FullName; }
        }

        /// <summary>
        /// Gets the string representing the extension part of the file.
        /// </summary>
        /// <value>
        /// The Extension property returns the FileSystemInfo extension, including the period (.).
        /// For example, for a file c:\NewFile.txt, this property returns ".txt".
        /// </value>
        string IFileSystemInfo.Extension
        {
            get { return InternalFileInfo.Extension; }
        }

        /// <summary>
        /// Gets or sets the creation time of the current file or directory.
        /// </summary>
        /// <value>
        /// The creation date and time of the current FileSystemInfo object.
        /// </value>
        /// <exception cref="T:System.PlatformNotSupportedException">The current operating system is not Windows NT or later.</exception>
        /// <exception cref="IDirectoryNotFoundException">
        /// The specified path is invalid; for example, it is on an unmapped drive.
        /// </exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException">The caller attempts to set an invalid creation time.</exception>
        /// <exception cref="IIOException">Refresh cannot initialize the data.</exception>
        DateTime IFileSystemInfo.CreationTime
        {
            get { return InternalFileInfo.CreationTime; }

            set { InternalFileInfo.CreationTime = value; }
        }

        /// <summary>
        /// Gets or sets the creation time, in coordinated universal time (UTC), of the current file or directory.
        /// </summary>
        /// <value>
        /// The creation date and time in UTC format of the current FileSystemInfo object.
        /// </value>
        /// <exception cref="T:System.PlatformNotSupportedException">The current operating system is not Windows NT or later.</exception>
        /// <exception cref="IDirectoryNotFoundException">
        /// The specified path is invalid; for example, it is on an unmapped drive.
        /// </exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException">The caller attempts to set an invalid creation time.</exception>
        /// <exception cref="IIOException">Refresh cannot initialize the data.</exception>
        DateTime IFileSystemInfo.CreationTimeUtc
        {
            get { return InternalFileInfo.CreationTimeUtc; }

            set { InternalFileInfo.CreationTimeUtc = value; }
        }

        /// <summary>
        /// Gets or sets the time the current file or directory was last accessed.
        /// </summary>
        /// <value>
        /// The time that the current file or directory was last accessed.
        /// </value>
        /// <exception cref="T:System.PlatformNotSupportedException">The current operating system is not Windows NT or later.</exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException">The caller attempts to set an invalid creation time.</exception>
        /// <exception cref="IIOException">Refresh cannot initialize the data.</exception>
        DateTime IFileSystemInfo.LastAccessTime
        {
            get { return InternalFileInfo.LastAccessTime; }

            set { InternalFileInfo.LastAccessTime = value; }
        }

        /// <summary>
        /// Gets or sets the time, in coordinated universal time (UTC), that the current file or directory was last accessed.
        /// </summary>
        /// <value>
        /// The UTC time that the current file or directory was last accessed.
        /// </value>
        /// <exception cref="T:System.PlatformNotSupportedException">The current operating system is not Windows NT or later.</exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException">The caller attempts to set an invalid creation time.</exception>
        /// <exception cref="IIOException">Refresh cannot initialize the data.</exception>
        DateTime IFileSystemInfo.LastAccessTimeUtc
        {
            get { return InternalFileInfo.LastAccessTimeUtc; }

            set { InternalFileInfo.LastAccessTimeUtc = value; }
        }

        /// <summary>
        /// Gets or sets the time when the current file or directory was last written to.
        /// </summary>
        /// <value>
        /// The time the current file was last written.
        /// </value>
        /// <exception cref="T:System.PlatformNotSupportedException">The current operating system is not Windows NT or later.</exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException">The caller attempts to set an invalid creation time.</exception>
        /// <exception cref="IIOException">Refresh cannot initialize the data.</exception>
        DateTime IFileSystemInfo.LastWriteTime
        {
            get { return InternalFileInfo.LastWriteTime; }

            set { InternalFileInfo.LastWriteTime = value; }
        }

        /// <summary>
        /// Gets or sets the time, in coordinated universal time (UTC), when the current file or directory was last written to.
        /// </summary>
        /// <value>
        /// The UTC time when the current file was last written to.
        /// </value>
        /// <exception cref="T:System.PlatformNotSupportedException">The current operating system is not Windows NT or later.</exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException">The caller attempts to set an invalid creation time.</exception>
        /// <exception cref="IIOException">Refresh cannot initialize the data.</exception>
        DateTime IFileSystemInfo.LastWriteTimeUtc
        {
            get { return InternalFileInfo.LastWriteTimeUtc; }

            set { InternalFileInfo.LastWriteTimeUtc = value; }
        }

        /// <summary>
        /// Gets or sets the attributes for the current file or directory.
        /// </summary>
        /// <value>
        /// FileAttributes of the current FileSystemInfo.
        /// </value>
        /// <exception cref="IFileNotFoundException">The specified file does not exist.</exception>
        /// <exception cref="IDirectoryNotFoundException">
        /// The specified path is invalid; for example, it is on an unmapped drive.
        /// </exception>
        /// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission.</exception>
        /// <exception cref="T:System.ArgumentException">The caller attempts to set an invalid file attribute.</exception>
        /// <exception cref="IIOException">Refresh cannot initialize the data.</exception>
        FileAttributes IFileSystemInfo.Attributes
        {
            get { return Convert(InternalFileInfo.Attributes); }

            set { InternalFileInfo.Attributes = Convert(value); }
        }

        #endregion

        #region IFileInfo Methods

        /// <summary>
        /// Gets a FileSecurity object that encapsulates the access control list (ACL) entries for the file described by the current FileInfo object.
        /// </summary>
        /// <returns>
        /// A FileSecurity object that encapsulates the access control rules for the current file.
        /// </returns>
        FileSecurity IFileInfo.GetAccessControl()
        {
            return InternalFileInfo.GetAccessControl();
        }

        /// <summary>
        /// Gets a FileSecurity object that encapsulates the specified type of access control list (ACL) entries for the file described by the current FileInfo object.
        /// </summary>
        /// <param name="includeSections">One of the AccessControlSections values that specifies which group of access control entries to retrieve.</param>
        /// <returns>
        /// A FileSecurity object that encapsulates the access control rules for the current file.
        /// </returns>
        FileSecurity IFileInfo.GetAccessControl(AccessControlSections includeSections)
        {
            return InternalFileInfo.GetAccessControl(includeSections);
        }

        /// <summary>
        /// Applies access control list (ACL) entries described by a FileSecurity object to the file described by the current FileInfo object.
        /// </summary>
        /// <param name="fileSecurity">A FileSecurity object that describes an access control list (ACL) entry to apply to the current file.</param>
        void IFileInfo.SetAccessControl(FileSecurity fileSecurity)
        {
            InternalFileInfo.SetAccessControl(fileSecurity);
        }

        /// <summary>
        /// Creates a StreamReader with UTF8 encoding that reads from an existing text file.
        /// </summary>
        /// <returns>
        /// A new StreamReader with UTF8 encoding.
        /// </returns>
        IStreamReader IFileInfo.OpenText()
        {
            return Convert(InternalFileInfo.OpenText());
        }

        /// <summary>
        /// Creates a StreamWriter that writes a new text file.
        /// </summary>
        /// <returns>
        /// A new StreamWriter.
        /// </returns>
        IStreamWriter IFileInfo.CreateText()
        {
            return Convert(InternalFileInfo.CreateText());
        }

        /// <summary>
        /// Creates a StreamWriter that appends text to the file represented by this instance of the FileInfo.
        /// </summary>
        /// <returns>
        /// A new StreamWriter.
        /// </returns>
        IStreamWriter IFileInfo.AppendText()
        {
            return Convert(InternalFileInfo.AppendText());
        }

        /// <summary>
        /// Copies an existing file to a new file, disallowing the overwriting of an existing file.
        /// </summary>
        /// <param name="destFileName">The name of the new file to copy to.</param>
        /// <returns>
        /// A new file with a fully qualified path.
        /// </returns>
        IFileInfo IFileInfo.CopyTo(string destFileName)
        {
            return Convert(InternalFileInfo.CopyTo(destFileName));
        }

        /// <summary>
        /// Copies an existing file to a new file, allowing the overwriting of an existing file.
        /// </summary>
        /// <param name="destFileName">The name of the new file to copy to.</param>
        /// <param name="overwrite">true to allow an existing file to be overwritten; otherwise, false.</param>
        /// <returns>
        /// A new file, or an overwrite of an existing file if overwrite is true. If the file exists and overwrite is false, an IOException is thrown.
        /// </returns>
        IFileInfo IFileInfo.CopyTo(string destFileName, bool overwrite)
        {
            return Convert(InternalFileInfo.CopyTo(destFileName, overwrite));
        }

        /// <summary>
        /// Creates a file.
        /// </summary>
        /// <returns>
        /// A new file.
        /// </returns>
        IStream IFileInfo.Create()
        {
            return Convert(InternalFileInfo.Create());
        }

        /// <summary>
        /// Deletes a file or directory.
        /// </summary>
        /// <exception cref="IDirectoryNotFoundException">
        /// The specified path is invalid; for example, it is on an unmapped drive.
        /// </exception>
        /// <exception cref="IIOException">
        /// There is an open handle on the file or directory, and the operating system is Windows XP or earlier.
        /// This open handle can result from enumerating directories and files.
        /// For more information, see How to: Enumerate Directories and Files.
        /// </exception>
        void IFileSystemInfo.Delete()
        {
            InternalFileInfo.Delete();
        }

        /// <summary>
        /// Decrypts a file that was encrypted by the current account using the Encrypt method.
        /// </summary>
        void IFileInfo.Decrypt()
        {
            InternalFileInfo.Decrypt();
        }

        /// <summary>
        /// Encrypts a file so that only the account used to encrypt the file can decrypt it.
        /// </summary>
        void IFileInfo.Encrypt()
        {
            InternalFileInfo.Encrypt();
        }

        /// <summary>
        /// Opens a file in the specified mode.
        /// </summary>
        /// <param name="mode">A FileMode constant specifying the mode (for example, Open or Append) in which to open the file.</param>
        /// <returns>
        /// A file opened in the specified mode, with read/write access and unshared.
        /// </returns>
        IStream IFileInfo.Open(FileMode mode)
        {
            return Convert(InternalFileInfo.Open(Convert(mode)));
        }

        /// <summary>
        /// Opens a file in the specified mode with read, write, or read/write access.
        /// </summary>
        /// <param name="mode">A FileMode constant specifying the mode (for example, Open or Append) in which to open the file.</param>
        /// <param name="access">A FileAccess constant specifying whether to open the file with Read, Write, or ReadWrite file access.</param>
        /// <returns>
        /// A IStream object opened in the specified mode and access, and unshared.
        /// </returns>
        IStream IFileInfo.Open(FileMode mode, FileAccess access)
        {
            return Convert(InternalFileInfo.Open(Convert(mode), Convert(access)));
        }

        /// <summary>
        /// Opens a file in the specified mode with read, write, or read/write access and the specified sharing option.
        /// </summary>
        /// <param name="mode">A FileMode constant specifying the mode (for example, Open or Append) in which to open the file.</param>
        /// <param name="access">A FileAccess constant specifying whether to open the file with Read, Write, or ReadWrite file access.</param>
        /// <param name="share">A FileShare constant specifying the type of access other FileStream objects have to this file.</param>
        /// <returns>
        /// A IStream object opened with the specified mode, access, and sharing options.
        /// </returns>
        IStream IFileInfo.Open(FileMode mode, FileAccess access, FileShare share)
        {
            return Convert(InternalFileInfo.Open(Convert(mode), Convert(access), Convert(share)));
        }

        /// <summary>
        /// Creates a read-only IStream.
        /// </summary>
        /// <returns>
        /// A new read-only IStream object.
        /// </returns>
        IStream IFileInfo.OpenRead()
        {
            return Convert(InternalFileInfo.OpenRead());
        }

        /// <summary>
        /// Creates a write-only IStream.
        /// </summary>
        /// <returns>
        /// A write-only unshared IStream object for a new or existing file.
        /// </returns>
        IStream IFileInfo.OpenWrite()
        {
            return Convert(InternalFileInfo.OpenWrite());
        }

        /// <summary>
        /// Moves a specified file to a new location, providing the option to specify a new file name.
        /// </summary>
        /// <param name="destFileName">The path to move the file to, which can specify a different file name.</param>
        void IFileInfo.MoveTo(string destFileName)
        {
            InternalFileInfo.MoveTo(destFileName);
        }

        /// <summary>
        /// Replaces the contents of a specified file with the file described by the current FileInfo object,
        /// deleting the original file, and creating a backup of the replaced file.
        /// </summary>
        /// <param name="destinationFileName">The name of a file to replace with the current file.</param>
        /// <param name="destinationBackupFileName">The name of a file with which to create a backup of the file described by the destFileName parameter.</param>
        /// <returns>
        /// A FileInfo object that encapsulates information about the file described by the destFileName parameter.
        /// </returns>
        IFileInfo IFileInfo.Replace(string destinationFileName, string destinationBackupFileName)
        {
            return Convert(InternalFileInfo.Replace(destinationFileName, destinationBackupFileName));
        }

        /// <summary>
        /// Replaces the contents of a specified file with the file described by the current FileInfo object,
        /// deleting the original file, and creating a backup of the replaced file.
        /// Also specifies whether to ignore merge errors.
        /// </summary>
        /// <param name="destinationFileName">The name of a file to replace with the current file.</param>
        /// <param name="destinationBackupFileName">The name of a file with which to create a backup of the file described by the destFileName parameter.</param>
        /// <param name="ignoreMetadataErrors">true to ignore merge errors (such as attributes and ACLs) from the replaced file to the replacement file; otherwise false.</param>
        /// <returns>
        /// A FileInfo object that encapsulates information about the file described by the destFileName parameter.
        /// </returns>
        IFileInfo IFileInfo.Replace(string destinationFileName, string destinationBackupFileName,
                                    bool ignoreMetadataErrors)
        {
            return
                Convert(InternalFileInfo.Replace(destinationFileName, destinationBackupFileName, ignoreMetadataErrors));
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents this instance.
        /// </returns>
        string IFileInfo.ToString()
        {
            return InternalFileInfo.ToString();
        }

        #endregion

        #region IFileSystemInfo Methods

        /// <summary>
        /// Refreshes the state of the object.
        /// </summary>
        /// <exception cref="IIOException">A device such as a disk drive is not ready.</exception>
        void IFileSystemInfo.Refresh()
        {
            InternalFileInfo.Refresh();
        }

        #endregion

        #region IFileInfo Members

        /// <summary>
        /// Populates a <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with the data needed to serialize the target object.
        /// </summary>
        /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> to populate with data.</param>
        /// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for this serialization.</param>
        /// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission. </exception>
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            InternalFileInfo.GetObjectData(info, context);
        }

        #endregion

        #region Private Member

        /// <summary>
        /// Converts the specified directory info.
        /// </summary>
        /// <param name="directoryInfo">The directory info.</param>
        /// <returns>the convertet object</returns>
        private IDirectoryInfo Convert(global::System.IO.DirectoryInfo directoryInfo)
        {
            return IoConverter.Convert(directoryInfo);
        }

        /// <summary>
        /// Converts the specified file info.
        /// </summary>
        /// <param name="fileInfo">The file info.</param>
        /// <returns>the convertet object</returns>
        private IFileInfo Convert(global::System.IO.FileInfo fileInfo)
        {
            return IoConverter.Convert(fileInfo);
        }

        /// <summary>
        /// Converts the specified file attributes.
        /// </summary>
        /// <param name="fileAttributes">The file attributes.</param>
        /// <returns>the convertet object</returns>
        private FileAttributes Convert(global::System.IO.FileAttributes fileAttributes)
        {
            return IoConverter.Convert(fileAttributes);
        }

        /// <summary>
        /// Converts the specified file attributes.
        /// </summary>
        /// <param name="fileAttributes">The file attributes.</param>
        /// <returns>the convertet object</returns>
        private global::System.IO.FileAttributes Convert(FileAttributes fileAttributes)
        {
            return IoConverter.Convert(fileAttributes);
        }

        /// <summary>
        /// Converts the specified share.
        /// </summary>
        /// <param name="share">The share.</param>
        /// <returns>the convertet object</returns>
        private global::System.IO.FileShare Convert(FileShare share)
        {
            return IoConverter.Convert(share);
        }

        /// <summary>
        /// Converts the specified access.
        /// </summary>
        /// <param name="access">The access.</param>
        /// <returns>the convertet object</returns>
        private global::System.IO.FileAccess Convert(FileAccess access)
        {
            return IoConverter.Convert(access);
        }

        /// <summary>
        /// Converts the specified mode.
        /// </summary>
        /// <param name="mode">The mode.</param>
        /// <returns>the convertet object</returns>
        private global::System.IO.FileMode Convert(FileMode mode)
        {
            return IoConverter.Convert(mode);
        }

        /// <summary>
        /// Converts the specified file stream.
        /// </summary>
        /// <param name="fileStream">The file stream.</param>
        /// <returns>the convertet object</returns>
        private IStream Convert(FileStream fileStream)
        {
            return IoConverter.Convert(fileStream);
        }

        /// <summary>
        /// Converts the specified stream reader.
        /// </summary>
        /// <param name="streamReader">The stream reader.</param>
        /// <returns>the convertet object</returns>
        private IStreamReader Convert(global::System.IO.StreamReader streamReader)
        {
            return IoConverter.Convert(streamReader);
        }

        /// <summary>
        /// Converts the specified stream writer.
        /// </summary>
        /// <param name="streamWriter">The stream writer.</param>
        /// <returns>the convertet object</returns>
        private IStreamWriter Convert(global::System.IO.StreamWriter streamWriter)
        {
            return IoConverter.Convert(streamWriter);
        }

        #endregion
    }
}